package ext.mango.utils;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Logger2 implements mango.utils.Logger {
    private static final int LOG_TRACE = 0;
    private static final int LOG_DEBUG = 1;
    private static final int LOG_INFO = 2;
    private static final int LOG_WARN = 3;
    private static final int LOG_ERROR = 4;
    
    private static final ThreadLocal<String> INFO_PREFX = new ThreadLocal<String>();
    
    private static boolean start = false;
    private LinkedBlockingQueue<LogObj> queue = new LinkedBlockingQueue<LogObj>();
    private static final Map<String, Logger2> logs = new HashMap<String, Logger2>();
    private Log log;
    
    public static void start() {
        if(start) {
            return;
        }
        start = true;
        Thread t = new Thread(new Runnable() {
            public void run() {
                while(true) {
                    for(Iterator<Logger2> iter = logs.values().iterator(); iter.hasNext();) {
                        iter.next().tick();
                    }
                }
            }
        });
        t.setName("Logger");
        t.setDaemon(true);
        t.start();
    }
    
    private class LogObj {
        int type;
        Object msg;
        Throwable t;
        
        public LogObj(int type, Object msg, Throwable t) {
            this.type = type;
            this.msg = msg;
            this.t = t;
        }
        public LogObj(int type, Object msg) {
            this.type = type;
            this.msg = msg;
        }
    }

    private Logger2(Log log) {
        this.log = log;
    }
    
    public static Logger2 getLog(String log) {
        Logger2 l = logs.get(log);
        if(l == null) {
            l = new Logger2(LogFactory.getLog(log));
            logs.put(log, l);
        }
        return l;
    }
    
    public static void setInfo(Object... info) {
        StringBuilder sb = new StringBuilder();
        for (Object i : info) {
            sb.append(i).append("|");
        }
        INFO_PREFX.set(sb.toString());
    }
    
    public static String getInfo() {
        return INFO_PREFX.get() == null ? "" : INFO_PREFX.get();
    }

    public static void removeInfo() {
        INFO_PREFX.remove();
    }

    @Override
    public void trace(Object msg) {
        queue.offer(new LogObj(LOG_TRACE, getInfo() + msg));
    }

    @Override
    public void debug(Object msg) {
        queue.offer(new LogObj(LOG_DEBUG, getInfo() + msg));
    }

    @Override
    public void info(Object msg) {
        queue.offer(new LogObj(LOG_INFO, getInfo() + msg));
    }

    @Override
    public void info(Object msg, Throwable t) {
        queue.offer(new LogObj(LOG_INFO, getInfo() + msg, t));
    }

    @Override
    public void warn(Object msg) {
        queue.offer(new LogObj(LOG_WARN, getInfo() + msg));
    }

    @Override
    public void error(Object msg) {
        queue.offer(new LogObj(LOG_ERROR, getInfo() + msg));
    }

    @Override
    public void error(Object msg, Throwable t) {
        queue.offer(new LogObj(LOG_ERROR, getInfo() + msg, t));
    }

    public void tick() {

        if(queue.size() != 0) {
            LogObj o = queue.poll();
            switch (o.type) {
            case LOG_DEBUG:
                log.debug(o.msg);
                break;
            case LOG_INFO:
                if(o.t != null) {
                    log.info(o.msg, o.t);
                }
                else {
                    log.info(o.msg);
                }
                break;
            case LOG_ERROR:
                if(o.t != null) {
                    log.error(o.msg, o.t);
                }
                else {
                    log.error(o.msg);
                }
                break;
            case LOG_WARN:
                if(o.t != null) {
                    log.warn(o.msg, o.t);
                }
                else {
                    log.warn(o.msg);
                }
                break;
            case LOG_TRACE:
                log.trace(o.msg, o.t);
                break;
            default:
                break;
            }
        }
    }

}